In a typical virtualisation environment, a processing device such as a processor core is arranged to execute hypervisor software which supports the execution of multiple virtual machines on that processing device. Each virtual machine will have one or more applications running on a particular operating system, with the hypervisor software acting as an interface layer between the virtual machine and the underlying hardware to enable the provision of appropriate hardware support to the virtual machine. Via the hypervisor software layer each virtual machine gets a particular view of the system in which it resides, and in particular gets a particular view of the available hardware resources of the system. Each virtual machine operates independently of other virtual machines on the system, and indeed is not necessarily aware of the presence of other virtual machines.
Accordingly, in an example system, one virtual machine may be executed which runs a particular operating system, for example Microsoft Windows, whilst another virtual machine is executed running a different operating system, for example Linux.
When developing a virtualisation environment, it is frequently necessary to provide virtual devices, such devices being software versions of hardware devices that the software running inside a virtual machine then uses. Often, devices within the system are memory mapped, and in such cases the simplest way to emulate such a device as a virtual device is to have any memory access to that device produce an exception such as a data abort, which can then be trapped by the virtual device handler which can then emulate the desired behaviour.
However, such an approach gives rise to a potential performance issue, since there is a significant overhead in taking each abort, dispatching it to the appropriate routine, and then returning to the original program. In many situations, a significant number of device accesses may occur in quick succession, thereby significantly increasing the overhead if each access is handled in the above manner. This in turn can give rise to a significant adverse effect on performance, and can increase latency. As an example, when such an approach is used for the interrupt controller, it increases the delay before incoming interrupts can be handled.
It may be envisaged that one way to seek to alleviate this performance impact would be to map the virtual device to actual memory, such that the registers of the virtual device that would typically be read from or written to are mapped in memory so that they can be directly accessed. This would provide a performance improvement, since instead of issuing an abort the access can merely be performed in the standard manner, resulting in the content of the particular memory address being read in the event of a read to the virtual device, or data being written to the specified memory address in the event of a write to the virtual device. However, whilst this can potentially address the performance impact, it is a poor substitute for the earlier described technique, as it requires every possible device register to be evaluated in advance, and cannot cope with registers which change upon repeated accesses. Furthermore, it cannot directly implement any side effects that would naturally occur when accessing particular registers of a device. For example, considering as an example device a UART (Universal Asynchronous Receiver Transmitter) device, a write to a send register of the UART would typically cause some send operation to be performed. Clearly, the act of merely writing to an address in memory cannot achieve this, and instead it would be necessary to rely on the hypervisor software to later analyse the memory address in question to determine that it had changed, and to instigate the required operation.
Accordingly, it would be desirable to provide an improved technique for handling access requests within a virtualisation environment.